package graphics.datastructures.hierarchalbb;

import graphics.datastructures.BoundingBox;
import graphics.datastructures.Triangle;
import graphics.raytracing.Tracable;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

class BoxBuilder<T extends Tracable<? extends Tracable<T>>> {

	private List<T> _tracables;
	private BoundingBox<T> _box;

	BoxBuilder(Collection<T> tracables, BoundingBox<T> box, int leafsize) {
		_tracables = new ArrayList<T>(tracables);
		_box = box;
		_leafsize = leafsize;
	}

	Box<T> build() {
		return create_box(_tracables, XYZ.X, _box);
	}

	int _leafsize;

	private Box<T> create_box(List<T> tr, XYZ d, BoundingBox<T> current) {
		System.out.println(tr.size());
		if (tr.size() < _leafsize)
			return new LeafBox<T>(tr, current);
		float offset = calculate_offset(tr, d, current, tr);
		List<T> lefties = new ArrayList<T>();
		List<T> righties = new ArrayList<T>();
		BoundingBox<T>[] l_r = split(current, offset, d);
		BoundingBox<T> left = l_r[0];
		BoundingBox<T> right = l_r[1];
		BoundingBox<T> lr = new BoundingBox<T>(left);
		BoundingBox<T> rr = new BoundingBox<T>(right);
		int count = 0;
		for (T tracable : tr) {
			int c = 0;
			if (includes(tracable, left)) {
				lefties.add(tracable);
				c++;
				lr.rtf()[d.ordinal()] = Math.max(lr.rtf()[d.ordinal()],
						tracable.getBB()[1][d.ordinal()]);
			}
			if (includes(tracable, right)) {
				righties.add(tracable);
				c++;
				rr.bln()[d.ordinal()] = Math.min(rr.bln()[d.ordinal()],
						tracable.getBB()[0][d.ordinal()]);
			}
			if (c == 2)
				count++;
			if (c == 0)
				System.out.println("lost tries");

		}
		if (tr.size() < count * 2) {
			return new LeafBox<T>(tr, current);
		}
		return new DualBox<T>(create_box(lefties, d.next(), lr), create_box(
				righties, d.next(), rr), current);
	}

	@SuppressWarnings("unchecked")
	private static <T extends Tracable<? extends Tracable<?>>> BoundingBox<T>[] split(
			BoundingBox<T> current, float offset, XYZ d) {

		float[] rtf = Arrays.copyOf(current.rtf(), current.rtf().length);
		float[] bln = Arrays.copyOf(current.bln(), current.bln().length);
		rtf[d.ordinal()] = offset;
		bln[d.ordinal()] = offset;
		BoundingBox<T> left = new BoundingBox<T>(current.bln(), rtf);
		BoundingBox<T> right = new BoundingBox<T>(bln, current.rtf());
		return new BoundingBox[] { left, right };
	}

	private static<T extends Tracable<? extends Tracable<?>>> boolean includes(T t, BoundingBox<T> b) {
		// for(float[] r:((Triangle)t).tri())
		// if(b.testInclusion(r))
		// return true;
		return b.testInclusion(t.getBB()[0]) || b.testInclusion(t.getBB()[1]);

	}

	private static <T extends Tracable<? extends Tracable<?>>> float calculate_offset(
			List<T> tr, XYZ d, BoundingBox<T> current, Collection<T> tr2) {

		return (current.bln()[d.ordinal()] + current.rtf()[d.ordinal()]) / 2;
		// return (current.bln()[d.ordinal()]+current.rtf()[d.ordinal()])/2;
	}

}
